package pogvue.gui.hub;

//import java.awt.Desktop;
import java.net.URI;
import java.net.URL;

import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.IOException;
import java.util.*;

import javax.swing.text.Document;
import javax.swing.event.*;

import pogvue.gui.*;
import pogvue.gui.event.*;
import pogvue.io.*;
import pogvue.util.*;
import pogvue.datamodel.*;

import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;

/**
 *
 * @author Michele
 */

public class GFFTableModel extends AbstractTableModel implements ActionListener, DocumentListener {
  Vector feat;
  FeatureSelection fsel;

  String[] colnames = {"Name","Type1","Type2","Start","End","Score","Strand","Phase","HitName","UCSC","Button"};
  
  JTable table;
  JButton button;
  JButton ucsc;
  JTextField tf;
  JLabel jl;

  /** Creates a new instance of GFFTableModel */
  public GFFTableModel(Vector feat) {
    this.feat = feat;
    button = new JButton("pogvue");
    ucsc = new JButton("ucsc");
  }
  
  public void setTextField(JTextField tf) {
    this.tf = tf;
  }
  public JTextField getTextField() {
    return tf;
  }
  public void setJLabel(JLabel jl) {
    this.jl = jl;
  }
  public void setFeatureSelection(FeatureSelection fsel){
    this.fsel = fsel;
  }
  
  public int getRowCount() {
    if (fsel.getFeatures().size() > 0) {
      
      return fsel.getFeatures().size();
      
    } else {
      return feat.size();
    }
  }
  
  public int getColumnCount() {
    return colnames.length;
  }
  public String getColumnName(int col) {
    return colnames[col];
  }
  
  public Class getColumnClass(int col) {
    return getValueAt(0,col).getClass();
  }
  
  public Vector getAllFeatures() {
    return feat;
  }

  public void actionPerformed(ActionEvent e) {

    if (tf != null) {
      if (e.getSource() instanceof JButton ||
	  tf.getText().indexOf("ch") != 0) {
	searchTable(tf.getText());
      }
    }
  }
  public void searchTable(String str) {

    if (str.length() < 3) {
      fsel.removeAllFeatures();
      jl.setText("No features");
      return;
    }
    Vector found = new Vector();

    if (str.indexOf("chr") == 0 &&
	str.indexOf(":") > 0    &&
	str.indexOf("-") > 0) {
      System.out.println("This is a region string - deal");
      SequenceRegion sr = parseRegionString(str);

      if (sr != null) {
	Vector tmpfeat = overlaps(feat,sr.getChr(),sr.getStart(),sr.getEnd());
	fsel.removeAllFeatures();
	jl.setText(tmpfeat.size() + " features");
	fsel.addFeatures(tmpfeat);
      }

      return;
    }

    for (int i = 0;i < feat.size(); i++) {
      SequenceFeature f = (SequenceFeature)feat.elementAt(i);
      
      if (f.getId().indexOf(str) >= 0  ||
	  f.getPhase().indexOf(str) >=0) {
	found.addElement(f);
      }
      if (f.getHitFeature() != null &&
	  f.getHitFeature().getId().indexOf(str) >= 0) {
	found.addElement(f);
      }
    }

    if (found.size() > 0) {
      fsel.removeAllFeatures();
      jl.setText(found.size() + " features");
      fsel.addFeatures(found);
    }
  }
	  
      
  public Object getValueAt(int row, int col) {
    SequenceFeature f = (SequenceFeature)feat.elementAt(row);
    
    if (fsel.getFeatures().size() > 0) {
      f = (SequenceFeature)fsel.getFeatures().elementAt(row);
    }
    
    if (col == 0) {
      return f.getId();
    } else if (col == 1) {
      return f.getType();
    } else if (col == 2) {
      return f.getType2();
    } else if (col == 3) {
      return f.getStart();
    } else if (col == 4) {
      return f.getEnd();
    } else if (col == 5) {
      return f.getScore();
    } else if (col == 6) {
      return f.getStrand();
    } else if (col == 7){
      return f.getPhase();
    } else if (col == 8) {
      if (f.getHitFeature() != null){
	return f.getHitFeature().getId();
      } else {
	return "";
      }
    } else if (col == 9) {
      return button;
    } else if (col == 10) {
      return ucsc;
    }
    return "";
  }
  
  public void setTable(JTable t) {
    this.table = t;
  }
  
  public Vector getFeatures() {
    if (fsel.getFeatures().size() > 0) {
      return fsel.getFeatures();
    } else {
      return feat;
    }
  }
  
  public void insertUpdate(DocumentEvent e) {
    updateLog(e, "inserted into");
    //searchTable(tf.getText());
  }
  public void removeUpdate(DocumentEvent e) {
    updateLog(e, "removed from");
    //searchTable(tf.getText());
  }
  public void changedUpdate(DocumentEvent e) {
    updateLog(e, "changed from");
  }
  public void updateLog(DocumentEvent e, String action) {
    Document d = e.getDocument();
    
    System.out.println("Document " + d);
    
    if (d == tf.getDocument()) {
      String str = tf.getText();
      System.out.println("Text string is " + str + " " + action);
    }
    
  }

  public static void main(String[] args) {
    System.out.println("Args " + args);
    try {
	    
      Hashtable opts = GetOptions.get(args);
      System.out.println("Opts " + opts);
      String lenfile   = null;
      String gfffile   = null;	  
      String bandfile   = null;
      boolean dens     = false;
      boolean linked   = false;

      int    binsize   = 1000000;

      if (opts.containsKey("-lenfile")) {
	lenfile  = (String)opts.get("-lenfile");
      }
      if (opts.containsKey("-bandfile")) {
	bandfile  = (String)opts.get("-bandfile");
      }
      if (opts.containsKey("-gfffile")) {
	gfffile  = (String)opts.get("-gfffile");
      }        
      
      if (opts.containsKey("-density")) {
	dens  = true;
      }        
      if (opts.containsKey("-linked")) {
	linked  = true;
      }        
      if (opts.containsKey("-binsize")) {
	binsize = Integer.parseInt((String)opts.get("-binsize"));
      }
      
      // First the chromosomes

      FileParse file        = new FileParse(lenfile,"File");
      Vector    chromosomes = new Vector();
      CytoBandFile cbf = new CytoBandFile(bandfile,"File");
      Hashtable bands = cbf.getBands();
      
      
      String    line;
      
      while ((line = file.nextLine()) != null) {
	
	StringTokenizer str  = new StringTokenizer(line,"\t" );
	String          name = str.nextToken();
	int             len  = Integer.parseInt(str.nextToken());
	
	if (name.indexOf("_random") == -1 &&
	    name.indexOf("_NT_") == -1) {
	  Chromosome chr = new Chromosome(len,(Vector)bands.get(name),name);
	  chromosomes.addElement(chr);
	}
      }
      
      GFFFile gff  = new GFFFile(gfffile,"File");
      
      Vector  feat = gff.getFeatures();
      
      Hashtable chrhash = new Hashtable();
      
      for (int i = 0 ; i < chromosomes.size(); i++) {
	Chromosome chr = (Chromosome)chromosomes.elementAt(i);
	if (feat.size() > 0) {
	  chr.setDensity(dens); 
	  chr.addFeatures(feat,true);
	  chr.featureDensity(binsize);
	} else {
	  chromosomes.removeElement(chr);
	}
      }
      
      System.out.println("Chromosomes " + chromosomes.size());
      
      KaryotypePanel kp   = new KaryotypePanel(chromosomes);
      JScrollPane    jsp2 = new JScrollPane(kp);
      JTextField     tf   = new JTextField();
      JButton        bt   = new JButton("Search");
      
      
      
	  kp.setLinkedMaxcount(linked);
	  kp.setScrollPane(jsp2);
	  
	  Vector topfeat = new Vector();
	  Vector lowfeat = new Vector();
	  
	  for (int i = 0; i < feat.size(); i++) {
	    if (((SequenceFeature)feat.elementAt(i)).getType().equals("gene")) {
	      topfeat.addElement(feat.elementAt(i));
	      
	    } else {
	      lowfeat.addElement(feat.elementAt(i));
	    }
	  }
	  for (int i = 0 ; i < lowfeat.size();i++) {
	    topfeat.addElement(lowfeat.elementAt(i));
	  }
	  
	  GFFTableModel    tm    = new GFFTableModel(topfeat);
	  JTable           table = new JTable(tm);
	  FeatureSelection fs    = new FeatureSelection(kp,table); 
	  
	  bt.addActionListener(tm);
	  tf.getDocument().addDocumentListener(tm);
	  
	  kp.setFeatureSelection(fs);
	  tm.setFeatureSelection(fs);


	  table.addMouseListener(fs);
	  
	  //Now the renderer

	  TableCellRenderer renderer = table.getDefaultRenderer(JButton.class);

	  table.setDefaultRenderer(JButton.class, new GFFTableButtonRenderer(renderer));
	  table.addMouseListener(new GFFTableButtonMouseListener(table));
	  
	  //table.setFillsViewportHeight(true);
	  //table.setAutoCreateRowSorter(true);
	  table.getSelectionModel().addListSelectionListener(fs);
          
	  tm.setTable(table);
          
	  // The Pfam renderer
	  TableColumnModel tcm = table.getColumnModel();
	  TableColumn tc = tcm.getColumn(7);
	  //tc.setCellRenderer(new PfamRenderer());
            
	  JFrame      jf  = new JFrame();
	  JScrollPane sp  = new JScrollPane(table);

	  JPanel contain1 = new JPanel();
	  JLabel jl = new JLabel("");
	  tm.setTextField(tf);
	  tm.setJLabel(jl);

	  contain1.setLayout(new BorderLayout());

	  contain1.add("Center",tf);
	  contain1.add("East",jl);
	  contain1.add("West",bt);
	  
	  JPanel contain2 = new JPanel();
	  contain2.setLayout(new BorderLayout());

	  contain2.add("Center",sp);
	  contain2.add("North",contain1);

	  JSplitPane  jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,jsp2,contain2);
	  
	  jsp.setOneTouchExpandable(true);
	  jsp.setDividerLocation(600);
	  
	  jf.getContentPane().add(jsp);
          
	  Dimension sd = Toolkit.getDefaultToolkit().getScreenSize();
	  
	  jf.setLocation(sd.width  / 2 - 700 / 2,
			 sd.height / 2 - 700 / 2);
	  
	  jf.setSize(800,800);
	  
	  jf.setVisible(true);
	  
	  
	    
	
	} catch (IOException e) {
	    System.out.println("ERROR: " + e);
	}
	
    }

  public Vector  overlaps(Vector feat, String chr, int start, int end) {
    Vector out = new Vector();
    
    for (int i = 0; i < feat.size(); i++) {
      SequenceFeature sf = (SequenceFeature)feat.elementAt(i);
      
      if (sf.getId().equals(chr)) {
	if (!(sf.getStart() > end || sf.getEnd() < start)) {
	    out.addElement(sf);
	}
      }
    }
    return out;
  }
  public SequenceRegion parseRegionString(String str) {

    
    int pos1 = str.indexOf(":");
    int pos2 = str.indexOf("-",pos1);
    
    if (pos1 > 0 && pos2 > 0 &&
	str.length() > pos2+1) {
      System.out.println("String " + str.length() + " " + pos2);
      String chr = str.substring(0,pos1);
      System.out.println(str.substring(pos1+1,pos2));
      System.out.println(str.substring(pos2+1));
      Integer start = Integer.parseInt(str.substring(pos1+1,pos2));
      Integer end   = Integer.parseInt(str.substring(pos2+1));
      
      SequenceRegion sr = new SequenceRegion(chr,start,end);

      return sr;
    }
  
    return null;
  }


}

class GFFTableButtonRenderer implements TableCellRenderer {
    private TableCellRenderer renderer;
    
    public GFFTableButtonRenderer(TableCellRenderer renderer) {
	this.renderer = renderer;
    }
    public Component getTableCellRendererComponent(JTable table, 
						   Object value,
						   boolean isSelected,	
						   boolean hasFocus,
						   int row, 
						   int column) {
	if (value instanceof Component){
	    return (Component)value;
	}else { 
	    return renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
	}
    }

 }   

class GFFTableButtonMouseListener implements MouseListener {
  private JTable table;
  private JFrame jf;
  private AlignSplitPanel asp;

    private void forwardEventToButton(MouseEvent e) {
	TableColumnModel columnModel = table.getColumnModel();
	int              column      = columnModel.getColumnIndexAtX(e.getX());
	int              row         = e.getY() / table.getRowHeight();
	Object           value;
	JButton          button;
	MouseEvent       buttonEvent;
	
	if(row >= table.getRowCount() || 
	   row < 0 ||
	   column >= table.getColumnCount() || 
	   column < 0) {
	    return;
	}   
	
	value = table.getValueAt(row, column);
	
	if(!(value instanceof JButton)) {
	    return;
	}  

	button = (JButton)value;
	
	buttonEvent =      (MouseEvent)SwingUtilities.convertMouseEvent(table, e, button);
	button.dispatchEvent(buttonEvent);

	// This is necessary so that when a button is pressed and released
	// it gets rendered properly.  Otherwise, the button may still appear
	// pressed down when it has been released.
	
	table.repaint();  
	table.revalidate();
    }
    
    public GFFTableButtonMouseListener(JTable table) {
	this.table = table;
    }

    public void mouseClicked(MouseEvent e) {
	int row = table.getSelectedRow();
	int col = table.getSelectedColumn();

	GFFTableModel tm = (GFFTableModel)table.getModel();
	
	String chr  = (String)tm.getValueAt(row,0);

	int    start = Integer.valueOf((Integer)tm.getValueAt(row,3));
	int    end   = Integer.valueOf((Integer)tm.getValueAt(row,4));
	  
	int tmpstart = start;
	int tmpend   = end;
	
	if (col == 9) {	

		System.out.println("String " + chr + " " + start + " " + end);
		
		tmpstart = start - 200000;
		tmpend   = end   + 200000;
		
		if (tmpstart < 1) {
		    tmpstart = 1;
		}

		start = tmpstart;
		end   = tmpend;

		SequenceRegion sr = tm.parseRegionString(tm.getTextField().getText());
		Alignment al = null;

		if (sr != null) {
		  if (jf  != null) {
		    Cursor cursor = jf.getCursor();
		    //setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		    jf.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		  }
		  al  = GenomeInfoFactory.requestRegion(sr.getChr(),sr.getStart(),sr.getEnd(),new Vector());
		  if (jf  != null) {
		    jf.setCursor(Cursor.getDefaultCursor());
		  }
		} else {
		  Vector tmpfeat = overlaps(tm.getFeatures(),chr,start,end);
		  Vector newfeat = new Vector();

		  for (int i = 0; i < tmpfeat.size(); i++) {
		      SequenceFeature sf1 = (SequenceFeature)tmpfeat.elementAt(i);
		      SequenceFeature sf2 = new SequenceFeature(null,sf1.getType(),sf1.getStart(),sf1.getEnd(),"");
		      sf2.setId(sf1.getId());
		      sf2.setStrand(sf1.getStrand());
		      sf2.setScore(sf1.getScore());
		      newfeat.addElement(sf2);
		  }
		  if (jf != null) {
		    jf.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		  }
		  al  = GenomeInfoFactory.requestRegion(chr,start,end,newfeat);
		  if (jf != null) {
		    jf.setCursor(Cursor.getDefaultCursor());
		  }


		}


		// Now finally we get to create the frame.

		if (al != null) {
		  updateFrame(al,start,start,end);
		  forwardEventToButton(e);  
		}
		
	} else if (col == 10) {
	    // show ucsc page in browser
	    tmpstart = start - 10000;
	    if (tmpstart < 1) {
		tmpstart = 1;
	    }
	  
	    String url = "http://genome.ucsc.edu/cgi-bin/hgTracks?db=hg18&position=" + chr + ":" + tmpstart + "-" + (end+10000);
	  try {

	    showInBrowser(new URL(url));
	    
	  } catch (java.io.IOException e2) {
	      System.out.println("IO Error for " + url);
	      e2.printStackTrace();
	  }
	}
    }

  public void updateFrame(Alignment al, int offset, int start, int end) {
	if (jf == null) {
	    jf = new JFrame("Pogvue");
	    jf.setSize(1500,1000);
	} 
	if (asp != null) {
	    asp.setAlignment(al);
	} else {
		    
	  asp = GenomeInfoFactory.makePanel(al,"Pogvue",.001,.1,null,offset,start,end,1350);	    

	  jf.getContentPane().add(asp);
	}
		

	jf.setVisible(true);

	asp.getAlignmentPanel2().getAlignViewport().getController().handleAlignViewportEvent(new AlignViewportEvent(this,asp.getAlignmentPanel2().getAlignViewport(),AlignViewportEvent.COLOURING));


	asp.getAlignmentPanel1().getAlignViewport().getController().handleAlignViewportEvent(new AlignViewportEvent(this,asp.getAlignmentPanel1().getAlignViewport(),AlignViewportEvent.COLOURING));
	asp.revalidate();
	asp.repaint();
	
    }

    public void mouseEntered(MouseEvent e) {
	forwardEventToButton(e);
    }
    public void mouseExited(MouseEvent e) {
	forwardEventToButton(e);  }
    public void mousePressed(MouseEvent e) {
	forwardEventToButton(e);
    }
    public void mouseReleased(MouseEvent e) {
	forwardEventToButton(e);
    }

  final static void showInBrowser(URL url) {
    try {
      //if (microsoftBrowser && windowsOS)
      //	Runtime.getRuntime().exec("iexplore.exe " + url);
      //      else if (firefoxBrowser && windowsOS)
      //Runtime.getRuntime().exec("firefox.exe \"" + url + "\"");
      //else if (firefoxBrowser && macOS) 

      String os = System.getProperty("os.name");
      System.out.println("OS name " + os);

      if (os.equals("Linux")) {
	String cmd = "firefox -url " + url.toString();
	Process p = Runtime.getRuntime().exec(cmd);
      } else if (os.equals("Mac OS X")) {
	  Runtime.getRuntime().exec(new String[] {"open", url.toString()});
	  //Runtime.getRuntime().exec(new String[] {"open", "-a", "Firefox.app", url.toString()});
	  //else 
	  //appletContext.showDocument(url, "_blank");
	}
    } catch (Exception e){
      System.out.println("Couldn't show in browser: " + e);
    }
  }

  public GFF findPolyA(Alignment al, int strand) {

    String kmer = "AATAAA";
    if (strand == -1) {
      kmer = "TTTATT";
    }

    GFF gff = new GFF(kmer,"",1,2);

    for (int i = 0; i < 1; i++) {
      SequenceI seq = al.getSequenceAt(i);

      if (!(seq instanceof GFF)) {
	  int tmpstart = asp.getAlignmentPanel1().getAlignViewport().getStartRes();
	  int tmpend   = asp.getAlignmentPanel1().getAlignViewport().getEndRes();
	  Vector kmers = SearchPanel.findKmers(al.getSequenceAt(i),kmer,tmpstart,tmpend);

	  for (int ii = 0;ii < kmers.size(); ii++) {
	  int j = ((Integer)kmers.elementAt(ii)).intValue();
		    
	  gff.addFeature(new SequenceFeature(null,kmer,j+1,j+6,""));
	      
	}
      }
    }
    return gff;
  }

  public Vector  overlaps(Vector feat, String chr, int start, int end) {
    Vector out = new Vector();
    
    for (int i = 0; i < feat.size(); i++) {
      SequenceFeature sf = (SequenceFeature)feat.elementAt(i);
      
      if (sf.getId().equals(chr)) {
	if (!(sf.getStart() > end || sf.getEnd() < start)) {
	  if (!sf.getType().equals("exon")) {
	    out.addElement(sf);
	  }
	}
      }
    }
    return out;
  }
}
